﻿using System.Diagnostics;
using System.Reflection;
using D.Util.UtilIoc;
using D.Util.UtilMiniProfiler;
using FreeSql;
using FreeSql.Internal;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MySqlX.XDevAPI.Relational;
using Org.BouncyCastle.Crypto.Engines;
using Snowflake.Core;
using static Org.BouncyCastle.Math.EC.ECCurve;

namespace D.UtilFreesqlOrm
{
    public static class FreesqlOrm
    {

        /// <summary>
        /// 创建一个IdWorker，参数分别为机器标识和工作进程ID
        /// </summary>
        private static IdWorker worker = new IdWorker(1, 1);

        /// <summary>
        /// 获取一个新的id
        /// </summary>
        public static string NewId => FreesqlOrm.worker.NextId().ToString();

        /// <summary>
        /// Sqlsugar 上下文初始化
        /// </summary>
        /// <param name="services"></param>
        public static async void AddOrm(this IServiceCollection services, DbConfig dbConfig)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));

            if (dbConfig.IsCreateDb)
            {
                // 初始化数据库表结构及种子数据
                InitDatabase(dbConfig);
            }

            var freeSqlBuilder = new FreeSqlBuilder()
                       .UseNameConvert(NameConvertType.ToLower)
                       .UseConnectionString(GetDbType(dbConfig.DbType), dbConfig.ConnectionString)
                       .UseAutoSyncStructure(dbConfig.SyncStructure) //自动迁移实体的结构到数据库
                       .UseLazyLoading(false)
                       .UseNoneCommandParameter(true);

            var freeSql = freeSqlBuilder.Build();
            services.AddSingleton(freeSql);
            //注册UnitOfWork
            services.AddTransient<IFreesqlUnitOfWork, FreesqlUnitOfWork>();

            var res = await freeSql.Ado.ExecuteConnectTestAsync();
            if (res)
            {
                Util.UtilConsole.ConsoleUtil.Info($"数据库连接成功：{freeSql.Ado.MasterPool.Get().Id}");
            }
            else
            {
                Util.UtilConsole.ConsoleUtil.Err($"数据库连接失败！");
            }



            if (dbConfig.MonitorCommand)
            {
                // 监听sql
                SetDbAop(freeSql);
            }
        }

        /// <summary>
        /// 配置Aop
        /// </summary>
        /// <param name="db"></param>
        public static void SetDbAop(IFreeSql fsql)
        {
            //fsql.Aop.CommandBefore += (s, e) =>
            //{
            //    e.Command.CommandText = null;
            //};

            fsql.Aop.CommandAfter += (s, e) =>
            {

                var sql = e.Command.CommandText;
                var originColor = Console.ForegroundColor;
                if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Green;
                if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Yellow;
                if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Red;

                if (e.Exception != null)
                {
                    if (e.Exception?.Message == null) return;
                    originColor = Console.ForegroundColor;
                    Console.ForegroundColor = ConsoleColor.DarkRed;
                    //做一些日志记录的操作。以下为示例。
                    Trace.WriteLine($"Message:{e.Exception.Message}\r\nStackTrace:{e.Exception.StackTrace}\r\nCommandText:{e.Command.CommandText}");
                    Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + sql + "\r\n");
                    Console.ForegroundColor = originColor;

#if DEBUG
                    //MiniProfilerUtil.PrintToMiniProfiler("SqlSugar-Info", sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
#endif
                }
                else
                {
                    Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + sql + "\r\n");
                    //var pars = fsql.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
                    //Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + e.Command.Parameters + "\r\n");
                    Console.ForegroundColor = originColor;

#if DEBUG
                    //MiniProfilerUtil.PrintToMiniProfiler("SqlSugar-Error", $"{e.Exception.Message}{Environment.NewLine}{e.Command.CommandText}{pars}{Environment.NewLine}");
#endif
                }
            };
        }

        /// <summary>
        /// 初始化数据库
        /// </summary>
        /// <param name="db"></param>
        /// <param name="dbConfig"></param>
        private static async void InitDatabase(DbConfig dbConfig)
        {

            var dbType = GetDbType(dbConfig.DbType);
            if (!dbConfig.IsCreateDb || dbType == DataType.Sqlite)
            {
                return;
            }

            var db = new FreeSqlBuilder()
                    .UseConnectionString(dbType, dbConfig.CreateDbConn)
                    .Build();

            try
            {
                Console.WriteLine("\r\ncreate database started");
                await db.Ado.ExecuteNonQueryAsync(dbConfig.CreateDbSql);
                Console.WriteLine("create database succeed\r\n");
            }
            catch (Exception e)
            {
                Console.WriteLine($"create database failed.\n{e.Message}\r\n");
            }

            // 2. 获取所有实体表,批量创建表
            var assemblies = RuntimeHelper.GetAllAssemblies().ToList();
            assemblies.ForEach(assembly =>
            {
                Type[] types = assembly.GetTypes().Where(m => !m.IsInterface && !m.IsAbstract && m.IsClass && m.IsDefined(typeof(FreeSql.DataAnnotations.TableAttribute))).ToArray();
                if (types != null && types.Count() > 0)
                {
                    // 根据types创建表
                    db.CodeFirst.SyncStructure(types);
                }
            });
        }

        private static DataType GetDbType(int dataType)
        {
            if (dataType == 0)
            {
                return DataType.MySql;
            }
            else if (dataType == 1)
            {
                return DataType.SqlServer;
            }
            else if (dataType == 2)
            {
                return DataType.PostgreSQL;
            }
            else if (dataType == 3)
            {
                return DataType.Oracle;
            }
            else if (dataType == 4)
            {
                return DataType.Sqlite;
            }
            else if (dataType == 5)
            {
                return DataType.OdbcOracle;
            }
            else if (dataType == 6)
            {
                return DataType.OdbcSqlServer;
            }
            else if (dataType == 7)
            {
                return DataType.OdbcMySql;
            }
            else if (dataType == 8)
            {
                return DataType.OdbcPostgreSQL;
            }
            else if (dataType == 9)
            {
                return DataType.Odbc;
            }
            else if (dataType == 11)
            {
                return DataType.MsAccess;
            }
            else if (dataType == 12)
            {
                return DataType.Dameng;
            }
            else if (dataType == 14)
            {
                return DataType.ShenTong;
            }
            else if (dataType == 15)
            {
                return DataType.KingbaseES;
            }
            else if (dataType == 16)
            {
                return DataType.Firebird;
            }
            else if (dataType == 18)
            {
                return DataType.ClickHouse;
            }
            else if (dataType == 19)
            {
                return DataType.GBase;
            }
            else if (dataType == 20)
            {
                return DataType.QuestDb;
            }
            else if (dataType == 21)
            {
                return DataType.Xugu;
            }
            else if (dataType == 22)
            {
                return DataType.CustomOracle;
            }
            else if (dataType == 23)
            {
                return DataType.CustomSqlServer;
            }
            else if (dataType == 24)
            {
                return DataType.CustomMySql;
            }
            else if (dataType == 25)
            {
                return DataType.CustomPostgreSQL;
            }
            else if (dataType == 26)
            {
                return DataType.DuckDB;
            }
            else if (dataType == 27)
            {
                return DataType.TDengine;
            }
            else
            {
                return DataType.Custom;
            }

        }
    }
}